<!DOCTYPE html>
<html>

<head>
  <title>QR Code Correction</title>
  <style>
    body {
      font-family: sans-serif;
      display: flex;
      flex-direction: column;
      align-items: center;
    }

    #container {
      display: flex;
      gap: 20px;
      margin-top: 20px;
      flex-wrap: wrap;
      /* 允许换行在小屏幕上 */
      justify-content: center;
    }

    canvas {
      border: 1px solid #ccc;
      max-width: 100%;
      /* 限制 canvas 最大宽度 */
      height: auto;
      /* 保持宽高比 */
    }

    #controls {
      margin-bottom: 10px;
      text-align: center;
    }

    #status {
      margin-top: 10px;
      font-size: 1em;
      color: #333;
      min-height: 1.2em;
      /* 保持高度稳定 */
    }

    h2 {
      text-align: center;
    }
  </style>

</head>

<body>

  <h1>自动二维码透视矫正与裁剪</h1>

  <div id="controls">
    <input type="file" id="imageInput" accept="image/*">
  </div>

  <div id="status">请选择一张包含二维码的图片。</div>

  <div id="container">
    <div>
      <h2>原始图像 (jsQR 检测结果)</h2>
      <canvas id="sourceCanvas"></canvas>
    </div>

    <div>
      <h2>矫正并裁剪结果</h2>
      <canvas id="resultCanvas"></canvas>
    </div>
  </div>

  <!-- 引入 jsQR 库 -->
  <script src="./plugins/jsQR.js"></script>
  <script src="./plugins/glfx.js"></script>

  <script>
    document.addEventListener('DOMContentLoaded', () => {
      const imageInput = document.getElementById('imageInput');
      const sourceCanvas = document.getElementById('sourceCanvas');
      const resultCanvas = document.getElementById('resultCanvas');
      const statusDiv = document.getElementById('status');

      const sourceContext = sourceCanvas.getContext('2d');
      const resultContext = resultCanvas.getContext('2d');

      let fxCanvas = null; // glfx.js canvas 实例

      // --- glfx.js 画布初始化 ---
      // 尝试创建 glfx.js canvas (尽早创建并检查 WebGL 支持)
      function initializeGlfxCanvas () {
        if (!fxCanvas) {
          try {
            fxCanvas = fx.canvas();
            console.log("glfx.js canvas 初始化成功。", fxCanvas);
          } catch (e) {
            statusDiv.textContent = "您的浏览器不支持 WebGL，无法使用图像处理功能。";
            console.error("glfx.js 初始化失败:", e);
            return false;
          }
        }

        return true;
      }

      // 在页面加载时尝试初始化一次
      initializeGlfxCanvas();


      // --- 文件选择处理 ---
      imageInput.addEventListener('change', (event) => {
        const file = event.target.files[0];
        if (!file) {
          return;
        }

        if (!initializeGlfxCanvas()) { // 确保 glfx.js 初始化成功
          return;
        }

        statusDiv.textContent = "正在加载图片...";
        // 清空之前的结果 canvas
        resultContext.clearRect(0, 0, resultCanvas.width, resultCanvas.height);
        resultCanvas.width = 0; // 重置尺寸，清空内容
        resultCanvas.height = 0;


        const reader = new FileReader();
        reader.onload = (e) => {
          const img = new Image();
          img.onload = () => {
            console.log("图片加载完成，尺寸:", img.naturalWidth, "x", img.naturalHeight);
            // 在 sourceCanvas 上绘制图片
            sourceCanvas.width = img.naturalWidth;
            sourceCanvas.height = img.naturalHeight;
            sourceContext.drawImage(img, 0, 0, sourceCanvas.width, sourceCanvas.height);

            // 图片加载并绘制完成后，进行二维码检测和矫正
            processImageForQrCode(sourceCanvas);
          };
          img.onerror = () => {
            statusDiv.textContent = "无法加载图片。";
            console.error("图片加载失败。");
          };
          img.src = e.target.result;
        };
        reader.onerror = () => {
          statusDiv.textContent = "无法读取文件。";
          console.error("文件读取失败。");
        };
        reader.readAsDataURL(file);
      });

      // --- 图像处理：jsQR 检测 + glfx.js 矫正 ---
      function processImageForQrCode (canvas) {
        statusDiv.textContent = "正在检测二维码...";
        console.log("开始 jsQR 检测...");

        const context = canvas.getContext('2d');
        const imageData = context.getImageData(0, 0, canvas.width, canvas.height);

        // 使用 jsQR 检测二维码
        const code = jsQR(imageData.data, imageData.width, imageData.height, {
          // inversionAttempts: "dontInvert", // 可选参数，根据需要调整
        });

        console.log("jsQR 检测结果:", code);

        if (code) {
          statusDiv.textContent = "找到二维码！正在进行矫正...";
          console.log("二维码位置:", code.location);

          // jsQR 找到二维码，获取角点
          // jsQR 返回的角点顺序是 topLeft, topRight, bottomRight, bottomLeft
          const { topLeftCorner, topRightCorner, bottomRightCorner, bottomLeftCorner } = code.location;

          // 定义 glfx.js 需要的源四边形角点数组
          // glfx.js 的 perspective 滤镜需要 [x0, y0, x1, y1, x2, y2, x3, y3] 顺序
          // 对应左上、右上、右下、左下
          const sourcePointsForGlfx = [
            topLeftCorner.x, topLeftCorner.y,
            topRightCorner.x, topRightCorner.y,
            bottomRightCorner.x, bottomRightCorner.y,
            bottomLeftCorner.x, bottomLeftCorner.y
          ];

          // 在原始 canvas 上绘制检测到的二维码边框 (可选可视化)
          drawQrCodeOutline(context, code.location);

          // 应用 glfx.js 透视变换
          applyPerspectiveCorrection(canvas, sourcePointsForGlfx);

        } else {
          statusDiv.textContent = "未在图片中找到二维码。";
          // 清空检测时绘制的边框 (如果之前有的话)
          sourceContext.clearRect(0, 0, sourceCanvas.width, sourceCanvas.height);
          sourceContext.drawImage(imageInput.files[0] ? new Image().src = URL.createObjectURL(imageInput.files[0]) : '', 0, 0, sourceCanvas.width, sourceCanvas.height); // 重新绘制原始图片清除边框
          // 简单方法是重新加载或清除重绘
          const img = new Image();
          img.onload = () => {
            sourceContext.clearRect(0, 0, sourceCanvas.width, sourceCanvas.height);
            sourceContext.drawImage(img, 0, 0, sourceCanvas.width, sourceCanvas.height);
          }
          if (imageInput.files && imageInput.files[0]) {
            img.src = URL.createObjectURL(imageInput.files[0]);
          } else {
            // 如果没有文件，清空 sourceCanvas
            sourceContext.clearRect(0, 0, sourceCanvas.width, sourceCanvas.height);
          }


          resultContext.clearRect(0, 0, resultCanvas.width, resultCanvas.height); // 清空结果
          resultCanvas.width = 0;
          resultCanvas.height = 0;
        }
      }

      // --- 绘制 jsQR 检测到的二维码轮廓 (可视化) ---
      function drawQrCodeOutline (context, location) {
        context.strokeStyle = "red";
        context.lineWidth = 3; // 加粗边框以便看清
        context.beginPath();
        context.moveTo(location.topLeftCorner.x, location.topLeftCorner.y);
        context.lineTo(location.topRightCorner.x, location.topRightCorner.y);
        context.lineTo(location.bottomRightCorner.x, location.bottomRightCorner.y);
        context.lineTo(location.bottomLeftCorner.x, location.bottomLeftCorner.y);
        context.closePath();
        context.stroke();

        // 绘制角点（可选）
        context.fillStyle = "blue";
        [location.topLeftCorner, location.topRightCorner, location.bottomRightCorner, location.bottomLeftCorner].forEach(p => {
          context.beginPath();
          context.arc(p.x, p.y, 5, 0, Math.PI * 2);
          context.fill();
        });
      }


      // --- 应用 glfx.js 透视变换和裁剪 ---
      function applyPerspectiveCorrection (sourceCanvas, sourcePointsForGlfx) {
        if (!fxCanvas) {
          statusDiv.textContent = "glfx.js 初始化失败，无法进行图像处理。";
          console.error("glfx.js canvas 未初始化。");
          return;
        }

        console.log("开始应用 glfx.js 透视矫正...");

        // 计算矫正后输出图像的合适尺寸
        const p1 = { x: sourcePointsForGlfx[0], y: sourcePointsForGlfx[1] }; // 左上
        const p2 = { x: sourcePointsForGlfx[2], y: sourcePointsForGlfx[3] }; // 右上
        const p3 = { x: sourcePointsForGlfx[4], y: sourcePointsForGlfx[5] }; // 右下
        const p4 = { x: sourcePointsForGlfx[6], y: sourcePointsForGlfx[7] }; // 左下

        const width1 = Math.hypot(p1.x - p2.x, p1.y - p2.y); // 顶边长度
        const width2 = Math.hypot(p4.x - p3.x, p4.y - p3.y); // 底边长度
        const estimatedWidth = Math.max(width1, width2); // 取较长的作为宽度

        const height1 = Math.hypot(p1.x - p4.x, p1.y - p4.y); // 左边长度
        const height2 = Math.hypot(p2.x - p3.x, p2.y - p3.y); // 右边长度
        const estimatedHeight = Math.max(height1, height2); // 取较长的作为高度

        // 估算的目标尺寸
        const outputWidth = Math.round(estimatedWidth);
        const outputHeight = Math.round(estimatedHeight);

        console.log("估算的目标输出尺寸:", outputWidth, "x", outputHeight);

        // 基本尺寸检查
        const maxDimension = 2000; // 设定一个最大尺寸，避免过大导致问题
        if (outputWidth <= 0 || outputHeight <= 0 || !isFinite(outputWidth) || !isFinite(outputHeight) || outputWidth > maxDimension || outputHeight > maxDimension) {
          const errorMsg = `计算出的输出尺寸无效或过大: ${outputWidth}x${outputHeight}`;
          console.error(errorMsg);
          statusDiv.textContent = errorMsg;
          // 清空结果 canvas
          resultContext.clearRect(0, 0, resultCanvas.width, resultCanvas.height);
          resultCanvas.width = 0;
          resultCanvas.height = 0;
          return;
        }

        // 设置 glfx.js 画布的尺寸为估算的输出尺寸
        fxCanvas.width = outputWidth;
        fxCanvas.height = outputHeight;
        console.log("glfx.js 画布尺寸设置为:", fxCanvas.width, "x", fxCanvas.height);


        try {
          // 从原始 canvas 创建纹理
          // 确保 sourceCanvas 已经绘制了图像并且尺寸正确
          const texture = fxCanvas.texture(sourceCanvas);
          console.log("从源 canvas 创建纹理成功。");

          // 应用透视变换滤镜
          // glfx.js 的 perspective 滤镜参数是源四边形的角点
          fxCanvas.draw(texture).perspective(sourcePointsForGlfx).update();
          console.log("应用透视滤镜并更新 glfx.js canvas 成功。");

          // 将矫正后的图像绘制到结果 canvas 上
          resultCanvas.width = outputWidth;
          resultCanvas.height = outputHeight;
          resultContext.clearRect(0, 0, resultCanvas.width, resultCanvas.height); // 清空结果 canvas
          resultContext.drawImage(fxCanvas.canvas, 0, 0, outputWidth, outputHeight);
          console.log("将 glfx.js 结果绘制到结果 canvas 成功。");

          statusDiv.textContent = "二维码已矫正并裁剪。";

        } catch (e) {
          console.error("在应用 glfx.js 滤镜或绘制时发生错误:", e);
          statusDiv.textContent = "处理图像时发生错误，请检查控制台。";
          // 清空结果 canvas
          resultContext.clearRect(0, 0, resultCanvas.width, resultCanvas.height);
          resultCanvas.width = 0;
          resultCanvas.height = 0;
        }
      }

      // 计算两点之间距离的辅助函数 (已在顶部定义，无需重复)
      // function distance(p1, p2) { ... }
    });
  </script>
</body>

</html>